#!/usr/bin/python
'''
Created by Colin Gillespie <c.gillespie@ncl.ac.uk>
$LastChangedDate$

'''

__revision__ = '$Rev$'

import math
from swiginac import numeric
from memoize import memoize


@memoize
def factorial(n):
    '''
        input: 
            integer n
        returns n!
    '''
    
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")

    if n == 0:
        return numeric(1)

    return n*factorial(n-1)



@memoize   
def binomial(n, x):
    '''
        inputs: integers n, x
        output: binomial coeff(n, x)
    '''
    assert n >= 0
    assert x >= 0
    assert n >= x

    if n == x:
        return 1

    return factorial(n)/factorial(x)/factorial(n-x)
    
@memoize
def binomialM(i1_tup, i2_tup):
    '''
        Input: two tuples of equal length
            (i1_1, i1_2, .. , i1_k)
            (i2_1, i2_2, .. , i2_k)
        Output: bin(i1_1,i2_1)*bin(i1_2, i2_2)...
    '''
    assert len(i1_tup) == len(i2_tup)
    total = 1
    for i1, i2 in zip(i1_tup, i2_tup):
        total *= binomial(i1, i2)
    return total 
